/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

package service.reportCenter.packagingReport;

import com.openbrew.openbrewrf.ACB;
import com.openbrew.openbrewrf.ACT;
import com.openbrew.openbrewrf.Administration;
import com.openbrew.openbrewrf.Allocation;
import com.openbrew.openbrewrf.PSOLines;
import com.openbrew.openbrewrf.PendingSO;
import com.openbrew.openbrewrf.views.BrewView;
import com.openbrew.openbrewrf.views.FGIView;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.StreamingOutput;
import pdf.GenericPDFCreator;

/**
 *
 * @author chapmand
 */
@Stateless
@Path("com.openbrew.openbrewrf.packagingreport")
public class PackagingReportFacadeREST {
    @PersistenceContext(unitName = "com.openBrew_openBrewRF_war_1.0PU")
    private EntityManager em;
    
    final Date today = new Date();
    String textCSV = "";
    String headerCSV = "";
    String title = "";
    
    //Variables for list
    List<FGIGantt> currentFGIs = new ArrayList<FGIGantt>();
    List<FGIView> fgis = new ArrayList<FGIView>();
    List<BrewView> brewItems = new ArrayList<BrewView>();
    List<Allocation> allocations = new ArrayList<Allocation>();
    List<PendingSO> psos = new ArrayList<PendingSO>();
    
    @GET
    @Path("{brew}/{days}/{useCustomDays}")
    @Produces(MediaType.APPLICATION_XML)
    public List<FGIGantt> runReport(@PathParam("brew") int brewID, @PathParam("days") int daysOut, @PathParam("useCustomDays") boolean useCustomDays) {
        final BrewView brew = getBrewViewFromID(brewID);
        loadFGICSV(brew.getGanttFGI(), daysOut, brew, useCustomDays);
        return currentFGIs;
    }
    
    private void loadFGICSV(final String csv, final int daysOut, BrewView brew, final boolean useCustomDays){
        currentFGIs.removeAll(currentFGIs);
        String[] fields = csv.split(",");
        int index = 0;
        if(!csv.isEmpty()){
            while(index < fields.length){
                FGIView fgi = getFGIViewFromID(fields[index]);
                int qty = 0;
                float volume = 0;
                if(fgi.getFGIPackageID().isServingVessel()){
                    volume = Float.parseFloat(fields[index + 1]);
                } else {
                    qty = Integer.parseInt(fields[index + 1]);
                }
                FGIGantt fg = new FGIGantt();
                fg.setFgiID(fgi);
                fg.setFgiQty(qty);
                fg.setFgiVolume(volume);
                fg.setFgiDesc(fgi.getFGIDescription());
                currentFGIs.add(fg);
                index = index + 2;
            }
        }
        //Add any FGIs that are not included in this table for this type of beer.
        autoFillFGIs(brew, daysOut, useCustomDays);
    }
    
    private void addAllFGIs(BrewView brew) {
        loadFGIs();
        for(final FGIView fgi : fgis){
            if(fgi.getFGIBeerID().getbId() == brew.getbBeerID().getbId()){
                boolean add = true;
                for(final FGIGantt fggantt : currentFGIs){
                    if(fggantt.getFgiID().getFgiid() == fgi.getFgiid()){
                        add = false;
                    }
                }
                if(add==true){
                    FGIGantt fg = new FGIGantt();
                    fg.setFgiID(fgi);
                    fg.setFgiQty(0);
                    fg.setFgiVolume(0);
                    fg.setFgiDesc(fgi.getFGIDescription());
                    currentFGIs.add(fg);
                }
            }
        }
    }
    
    private void autoFillFGIs(final BrewView brew, final int daysOut, final boolean useCustomDays) {
        //Ensure all FGIs are loaded for product.
        addAllFGIs(brew);
        
        loadBrews();
        loadAllocations();
        loadPSOs();
        
        //Parse through FGIs to predict number of units needed to be packaged.
        //Parse through FGI table.
        for(int x=0; x<currentFGIs.size(); x++){
            FGIView fgi = currentFGIs.get(x).getFgiID();
            int inventory = (int)fgi.getFGIQtyOnHand();
            float invVolume = fgi.getServingTankVolume();
            
            //Set up calendar for each and every beer that is currently in production.
            Calendar c = Calendar.getInstance();
            c.setTime(today);
            c.set(Calendar.HOUR_OF_DAY, 0);
            c.set(Calendar.MINUTE, 0);
            c.set(Calendar.SECOND, 0);
            c.set(Calendar.MILLISECOND, 0); 
            c.add(Calendar.DATE, -30);
            Date date = new Date(c.getTime().getTime());
            
            Calendar cStop = Calendar.getInstance();
            cStop.setTime(today);
            cStop.set(Calendar.HOUR_OF_DAY, 0);
            cStop.set(Calendar.MINUTE, 0);
            cStop.set(Calendar.SECOND, 0);
            cStop.set(Calendar.MILLISECOND, 0); 
            cStop.add(Calendar.DATE, daysOut);
            Date stopDate = new Date(cStop.getTime().getTime());
            
            if(!useCustomDays){
                //Check if there is another brew besides this one upcomming, if so only watch until that date, if not go for 60 days.
                for(final BrewView brewItem : brewItems){
                    if(brewItem.getbState()< 20){
                        if(brewItem.getbBeerID().getbId()== fgi.getFGIBeerID().getbId()){
                            if(brewItem.getGanttStop()!=null){
                                if((brewItem.getGanttStop().after(today))&&(brewItem.getBid() != brew.getBid())){
                                    stopDate = brewItem.getGanttStop();
                                }
                            }
                        }
                    }
                }
            }
            
            final int daysTillNextBrew = getDaysBetween(brew);
            currentFGIs.get(x).setDaysTillNextBrew(daysTillNextBrew);
            
            //Now look through date by date until the stop date to determine what minimal inventory fills need to be.
            while((date.before(stopDate))){
                for(final BrewView brewItem : brewItems){
                    if((brewItem.getbState() < 20)
                            &&(brewItem.getbBeerID().getbId() == fgi.getFGIBeerID().getbId())
                            &&(brewItem.getBid()!= brew.getBid())){
                        //only look at beers not finished yet.
                        if(brew.getGanttStop()!=null && brew.getGanttStop().toString().equals(date.toString())){
                            //If it is on said date, add it to the inventory
                            inventory += getInventoryNumberFromGantt(brewItem, fgi);
                            invVolume += getInventoryVolumeFromGantt(brewItem, fgi);
                        }
                    }
                }
                for(final Allocation a : allocations){
                    if(a.getAllocationDueDate().equals(date)){
                        //If the sale date is on this day we need to subtract it from the allocation table.
                        inventory = inventory - getAllocationFGIQty(a, fgi);
                        invVolume = invVolume - getAllocationFGIVolume(a, fgi);
                    }
                }
                
                for(final PendingSO pso : psos){
                    if(pso.getPSODate().equals(date)){
                        inventory = inventory - getPendingSOQty(pso, fgi);
                    }
                }    
                //Increment Date
                c.add(Calendar.DATE, 1);
                date = c.getTime();
            }
            //Check to see how negative we are this is the number we need.
            if(inventory < 0){
                inventory = inventory * -1;
                currentFGIs.get(x).setFgiRec(inventory);
            }
            if(invVolume < 0){
                invVolume = invVolume * -1;
                currentFGIs.get(x).setFgiVolRec(invVolume);
            }
        }
    }
    
    private int getDaysBetween(final BrewView brew){
        int days = 60;
        for(final BrewView brewItem : brewItems){
            if(brewItem.getbBeerID().getbId()== brew.getbBeerID().getbId()){
                if(brewItem.getGanttStop()!=null && brew.getGanttStop()!=null){
                    if(brewItem.getGanttStop().after(brew.getGanttStop())){
                        int newDays = daysBetween(brew.getGanttStop(), brewItem.getGanttStop());
                        if(!(newDays<0)&&(newDays < days)){
                            days = newDays;
                        }
                    }
                }
            }
        }
        return days;
    }
    
    private int daysBetween(java.util.Date d1, java.util.Date d2){
        return (int)((d2.getTime() - d1.getTime()) / 86400000);
    }
    
    /***************************************************************************
     * FGI Functions
     **************************************************************************/
    private String getFGICSV(){
        String csv = "";
        for(final FGIGantt fg : currentFGIs){
            csv += Integer.toString(fg.getFgiID().getFgiid()) + "," + Integer.toString(fg.getFgiQty()) + ",";
        }
        if (csv.length() > 0) {
            csv = csv.substring(0, csv.length()-1);
            return csv;
        } else {
            return csv;
        } 
    }
    
    private List<FGIGantt> getFGICSVList(final BrewView brew){
        List<FGIGantt> fgicsv = new ArrayList<FGIGantt>();
        String[] fields = brew.getGanttFGI().split(",");
        int index = 0;
        if(!brew.getGanttFGI().isEmpty()){
            while(index < fields.length){
                FGIView fgi = getFGIViewFromID(fields[index]);
                final int qty = Integer.parseInt(fields[index + 1]);
                FGIGantt fg = new FGIGantt();
                fg.setFgiID(fgi);
                fg.setFgiQty(qty);
                fg.setFgiDesc(fgi.getFGIDescription());
                fgicsv.add(fg);
                index = index + 2;
            }
        }
        return fgicsv;
    }
    
    private int getInventoryNumberFromGantt(final BrewView brew, final FGIView fgi){
        List<FGIGantt> fgicsv = getFGICSVList(brew);
        for(final FGIGantt fgigantt : fgicsv){
            if(fgigantt.getFgiID().getFgiid() == fgi.getFgiid()){
                return fgigantt.getFgiQty();
            }
        }
        return 0;
    }
    
    private float getInventoryVolumeFromGantt(final BrewView brew, final FGIView fgi){
        List<FGIGantt> fgicsv = getFGICSVList(brew);
        for(final FGIGantt fgigantt : fgicsv){
            if(fgigantt.getFgiID().getFgiid() == fgi.getFgiid()){
                return fgigantt.getFgiVolume();
            }
        }
        return 0;
    }
    
    private int getAllocationFGIQty(final Allocation al, final FGIView fgi){
        int retVal = 0;
        if(al.getACTCollection()!=null){
            for(final ACT act : al.getACTCollection()){
                if(act.getACBCollection()!=null){
                    for(final ACB acb : act.getACBCollection()){
                        if(!acb.getACBFilled()){
                            final FGIView alFGI = acb.getACBFGI();
                            if(alFGI.getFgiid()== fgi.getFgiid()){
                                retVal += acb.getACBQty();
                            }
                        }
                    }
                }
            }
        }
        return retVal;
    }
    
    private float getAllocationFGIVolume(final Allocation al, final FGIView fgi){
        int retVal = 0;
        if(al.getACTCollection()!=null){
            for(final ACT act : al.getACTCollection()){
                if(act.getACBCollection()!=null){
                    for(final ACB acb : act.getACBCollection()){
                        if(!acb.getACBFilled()){
                            final FGIView alFGI = acb.getACBFGI();
                            if(alFGI.getFgiid() == fgi.getFgiid()){
                                retVal += acb.getServingTankVolume();
                            }
                        }
                    }
                }
            }
        }
        return retVal;
    }
    
    private int getPendingSOQty(final PendingSO pso, final FGIView fgi){
        int retVal = 0;
        for(final PSOLines psoLine : pso.getPSOLinesCollection()){
            if(!psoLine.getPSOSold()){
                if(psoLine.getPSOFGI().getFgiid()== fgi.getFgiid()){
                    retVal += psoLine.getPSOQty();
                }
            }
        }
        return retVal;
    }
    
    //Functions to access database items.
    private FGIView getFGIViewFromID(final String fgiID){
        final int id = Integer.parseInt(fgiID);
        TypedQuery<FGIView> query = em.createQuery(
                "SELECT f FROM FGIView f WHERE f.fgiid = ?1", FGIView.class);
        query.setParameter(1, id);
        return query.getSingleResult();
    }
    
    private BrewView getBrewViewFromID(final int brewID){
        TypedQuery<BrewView> query = em.createQuery(
                "SELECT b FROM BrewView b WHERE b.bid = ?1", BrewView.class);
        query.setParameter(1, brewID);
        return query.getSingleResult();
    }
    
    private void loadFGIs(){
        fgis.removeAll(fgis);
        fgis = getEntityManager().createNamedQuery("FGIView.findAll", FGIView.class).getResultList();
    }
    
    private void loadBrews(){
        brewItems.removeAll(brewItems);
        brewItems = getEntityManager().createNamedQuery("BrewView.findAllInProduction", BrewView.class).getResultList();
    }
    
    private void loadAllocations(){
        allocations.removeAll(allocations);
        allocations = getEntityManager().createNamedQuery("Allocation.findOpenAllocations", Allocation.class).getResultList();
    }
    
    private void loadPSOs(){
        psos.removeAll(psos);
        psos = getEntityManager().createNamedQuery("PendingSO.findOpenPSOs", PendingSO.class).getResultList();
    }

    @GET
    @Path("{id}/pkg_req_pdf")
    @Produces({"application/pdf"})
    public StreamingOutput findHeaderImg(@PathParam("id") Integer id) {
        final BrewView brew = getBrewViewFromID(id);
        
        //Build csv body
        textCSV = "";
        for(final FGIGantt fgi : getFGICSVList(brew)){
            textCSV += fgi.getFgiDesc() + "," + Integer.toString(fgi.getFgiQty()) + " units(s)\n";
        }
        
        //Build headers
        headerCSV = "Package Type:,Min. Number";
        
        //Build title
        title = "Batch: " + Integer.toString(brew.getBid()) + " - " + brew.getbBeerID().getbName() + " Package Requirements";
        
        //text += "Please Sign, Date, and File this req:";
        return new StreamingOutput() {
            @Override
            public void write(OutputStream output) throws IOException, WebApplicationException {
                try {
                    final GenericPDFCreator pdf = new GenericPDFCreator();
                    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
                    pdf.createTablePDF(output, headerCSV, textCSV, title);
                    byte[] bytes = outputStream.toByteArray();
                    output.write(bytes);
                } catch (IOException e) {
                    throw new WebApplicationException(e);
                }
            }
        };
    }
    
    protected EntityManager getEntityManager() {
        return em;
    }
}
